/*
 * Ingenic IMP emulation FrameSource.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Zoro <ykli@ingenic.cn>
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>

#include <constraints.h>
#include <imp/imp_log.h>
#include <imp/imp_system.h>
#include <imp/imp_framesource.h>
#include <imp/imp_utils.h>

#include <system/system.h>
#include <system/module.h>
#include <system/group.h>
#include <system/device.h>
#include <system/vbm.h>
#include <system/imp_v4l2.h>
#include <emulator/imp_emu_framesource.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "emu-Framesource"

//#define EMU_IMAGE_PATH "/mnt/nfs/"
#define EMU_IMAGE_PATH "./"
//#define EMU_IMAGE_PURE_GRAY

typedef enum {
	FSCHANNEL_STATE_CLOSE,
	FSCHANNEL_STATE_OPEN,
	FSCHANNEL_STATE_RUN,
} FSChannelState;

typedef struct {
	int index;
	FSChannelState state;
	IMPFSChnAttr attr;
	uint16_t out_width;
	uint16_t out_height;
	struct v4l2_format fmt;

	pthread_t tid;
	int img_fd;
	Group *group;
} FSChannel;

typedef struct {
	Device *device;

	FSChannelState state;
	FSChannel channel[NR_MAX_FS_CHNS];
} Framesource;

typedef struct {
	int index;
	uint32_t vaddr;
	int width;
	int height;
	int length;
	struct timeval timestamp;
} EmuV4L2Buffer;

typedef struct {
	uint16_t maxwidth;
	uint16_t maxheight;
	uint16_t minwidth;
	uint16_t minheight;
} EmuV4L2Scalercap;

typedef struct {
	uint16_t outwidth;
	uint16_t outheight;
} EmuV4L2Scaler;

typedef struct {
	int flag;
	sem_t semap;
	uint16_t start;
	uint16_t end;
	EmuV4L2Buffer buf[32];
} EmuBufFifo;


static Framesource *gFramesource = NULL;
static EmuBufFifo g_emu_vb[NR_MAX_FS_CHNS];

static Framesource *get_framesource(void)
{
	return gFramesource;
}

static int v4l2_emu_cropcap(uint32_t chn_num, struct v4l2_cropcap *a)
{
	int ret = 0;
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];

	a->type = 1;
	a->pixelaspect.numerator = 1;
	a->pixelaspect.denominator = 1;
	a->bounds.left = 0;
	a->bounds.top = 0;
	a->bounds.width = 2592;
	a->bounds.height = 1926;

	a->defrect.left = 0;
	a->defrect.top = 0;
	switch(chn_num){
		case 0:
			a->defrect.width = 2592;
			a->defrect.height = 1926;
			break;
		case 1:
			a->defrect.width = 1920;
			a->defrect.height = 1080;
			break;
		case 2:
			a->defrect.width = 1280;
			a->defrect.height = 720;
			break;
		default:
			ret = -1;
			break;
	}
	channel->out_width = a->defrect.width;
	channel->out_height = a->defrect.height;
	return ret;
}

static int v4l2_emu_set_crop(uint32_t chn_num, struct v4l2_crop *a)
{
	int ret = 0;
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];
	IMPFSChnAttr *attr = &channel->attr;
	attr->crop.left = a->c.left;
	attr->crop.top = a->c.top;
	attr->crop.width = a->c.width;
	attr->crop.height = a->c.height;
	channel->out_width = a->c.width;
	channel->out_height = a->c.height;
	return ret;
}

static int v4l2_emu_scalercap(uint32_t chn_num, EmuV4L2Scalercap *sc)
{
	int ret = 0;
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];

	sc->maxwidth = channel->out_width;
	sc->maxheight = channel->out_height;
	sc->minwidth = 128;
	sc->minheight = 128;
	return ret;
}

static int v4l2_emu_set_scaler(uint32_t chn_num, EmuV4L2Scaler *sc)
{
	int ret = 0;
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];
	channel->out_width = sc->outwidth;
	channel->out_height = sc->outheight;
//	IMP_LOG_DBG(TAG, "%s():[chn%d] outwidth = %d outheight = %d!\n",__func__, chn_num,
//					channel->out_width, channel->out_height);
	return ret;
}

static int v4l2_emu_try_fmt(uint32_t chn_num, struct v4l2_format *f)
{
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];

	if(f->fmt.pix.width != channel->out_width ||
		f->fmt.pix.height != channel->out_height)
		return -1;

	return 0;
}

static int v4l2_emu_set_fmt(uint32_t chn_num, struct v4l2_format *f)
{
	int bits = 0;
	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chn_num];

	if(f->fmt.pix.width != channel->out_width ||
		f->fmt.pix.height != channel->out_height)
		return -1;
	switch(f->fmt.pix.pixelformat){
		case V4L2_PIX_FMT_NV12:
			bits = 12;
			break;
		case V4L2_PIX_FMT_YUYV:
			bits = 16;
			break;
		default:
			break;
	}

	f->fmt.pix.bytesperline = f->fmt.pix.width * bits / 8;
	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
	return 0;
}

static int v4l2_emu_qbuf(EmuV4L2Buffer *vb, int fd)
{
	int i = 0;
	EmuBufFifo *fifo = NULL;
	for(i = 0; i < NR_MAX_FS_CHNS; i++){
		if(g_emu_vb[i].flag == fd){
			fifo = &g_emu_vb[i];
			break;
		}
	}
	if(fifo){
		fifo->end = fifo->end % 32;
		fifo->buf[fifo->end].index = vb->index;
		fifo->buf[fifo->end].vaddr = vb->vaddr;
		fifo->buf[fifo->end].width = vb->width;
		fifo->buf[fifo->end].height = vb->height;
		fifo->buf[fifo->end].length = vb->length;
		fifo->end++;
		sem_post(&fifo->semap);
//		printf("%s[%d] end = %d vaddr = 0x%08x length = %d\n",__func__, __LINE__,fifo->end, vb->vaddr, vb->length);
	}

	return 0;
}

static int v4l2_emu_dqbuf(EmuV4L2Buffer *vb, int fd)
{
	int i = 0;
	EmuBufFifo *fifo = NULL;
	for(i = 0; i < NR_MAX_FS_CHNS; i++){
		if(g_emu_vb[i].flag == fd){
			fifo = &g_emu_vb[i];
			break;
		}
	}
	if (!fifo) {
		IMP_LOG_ERR(TAG, "Emu buffer hasn't inited yet\n");
		return -1;
	}

	sem_wait(&fifo->semap);
	fifo->start = fifo->start % 32;
	vb->index = fifo->buf[fifo->start].index;
	vb->vaddr = fifo->buf[fifo->start].vaddr;
	vb->width = fifo->buf[fifo->start].width;
	vb->height = fifo->buf[fifo->start].height;
	vb->length = fifo->buf[fifo->start].length;
	fifo->start++;
	gettimeofday(&vb->timestamp, NULL);
	if (fd > 0) {
		static int picture_idx = 0;

		int file_length = lseek(fd, 0, SEEK_END);

#ifdef EMU_IMAGE_PURE_GRAY
		int nr_frames = file_length / (vb->width * vb->height);
#else
		int nr_frames = file_length / vb->length;
#endif

#ifdef EMU_IMAGE_PURE_GRAY
		int pic_start = (picture_idx % nr_frames) * (vb->width * vb->height);
#else
		int pic_start = (picture_idx % nr_frames) * vb->length;
#endif
		lseek(fd, pic_start, SEEK_SET);

		int ret;
#ifdef EMU_IMAGE_PURE_GRAY
		ret = read(fd, (void *)vb->vaddr, (vb->width * vb->height));
		if (ret != (vb->width * vb->height)) {
#else
		ret = read(fd, (void *)vb->vaddr, vb->length);
		if (ret != vb->length) {
#endif
			IMP_LOG_ERR(TAG, "read image file error\n");
		}

		picture_idx++;
		//printf("%s[%d] start = %d vaddr = 0x%08x vb->width * vb->height = %d\n",__func__, __LINE__,fifo->start, vb->vaddr, vb->width * vb->height);
	} else {
		/* No image file, so we draw sth. Haha. */
#if 0
		int i;
		for (i = 0; i < vb->length; i++) {
			uint8_t *addr = (uint8_t *)vb->vaddr;
			if (((i % 128) < 64) && ((vb->index % 2) == 0))
				*addr = 0xff;
			else
				*addr = 0;
		}
#endif
	}

	return 0;
}

//static int get_frame(int *vb_idx, uint64_t *time, void *pri)
static int get_frame(IMPFrameInfo *frame, void *pri)
{
#if 1
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];
	EmuV4L2Buffer buf;
	memset(&buf, 0, sizeof(buf));

	v4l2_emu_dqbuf(&buf, channel->img_fd);

	frame->index = buf.index;
	frame->timeStamp = (int64_t)((int64_t)buf.timestamp.tv_sec * 1000000 + (int64_t)buf.timestamp.tv_usec);

	IMP_LOG_VERBOSE(TAG, "get cluster:%d \n", buf.index);
#endif
	return 0;
}

static int release_Frame(IMPFrameInfo *frame, void *pri)
{
#if 1
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];
	EmuV4L2Buffer buf;
	memset(&buf, 0, sizeof(buf));

	buf.index = frame->index;
	buf.vaddr = frame->virAddr;
	buf.width = frame->width;
	buf.height = frame->height;
	buf.length = frame->size;
	v4l2_emu_qbuf(&buf, channel->img_fd);

	IMP_LOG_VERBOSE(TAG, "release cluster:%d \n", buf.index);
#endif
	return 0;
}

static void *frame_pooling_thread(void *p)
{
	FSChannel *channel = (FSChannel *)p;

	while (1) {
		IMP_LOG_VERBOSE(TAG, "Tick\n");

		Group *group = channel->group;
		group_tick(group);
		usleep(1000000 / channel->attr.outFrmRateNum / channel->attr.outFrmRateDen);
	}

	return NULL;
}

static Framesource *alloc_framesource(void)
{
	Framesource *framesource;
	Device *dev = alloc_device("EmuFrameSource", sizeof(Framesource));

	if (dev == NULL) {
		IMP_LOG_ERR(TAG, "alloc_device() error\n");
		return NULL;
	}
	dev->nr_groups = NR_MAX_FS_GROUPS;
	dev->dev_id = DEV_ID_EMU_FS;

	framesource = device_pri(dev);
	framesource->device = dev;

	return framesource;
}

static void free_framesource(Framesource *framesource)
{
	Device *dev = framesource->device;
	free_device(dev);
}

static int on_framesource_group_data_update(Group *group, IMPFrameInfo *d)
{
	Device *dev = get_device_of_group(group);
	Framesource *framesource = (Framesource *)device_pri(dev);
	int i = 0;

	FSChannel *chn = &framesource->channel[group->group_index];
	if (chn->state != FSCHANNEL_STATE_RUN) {
		IMP_LOG_INFO(TAG, "framesource->channel[%d].state=%d\n", group->group_index, framesource->channel[group->group_index], chn->state);
		return -1;
	}
	IMP_LOG_VERBOSE(TAG, "[%s][%s] update\n",
					framesource->device->name, group->module->name);

	IMPFrameInfo *frame;
	int ret;
	ret = VBMGetFrame(group->group_index, &frame);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "VBMGetFrame() error:%d\n", ret);
		return -1;
	}
	for (i = 0; i < group->module->num_of_observer; i++) {
		VBMLockFrame(frame);
	}

	group->for_output_data[0] = frame;

	return 0;
}

static int framesource_create_group(int group_index)
{
	Framesource *framesource = get_framesource();
	Device *dev = framesource->device;

	if (group_index > NR_MAX_FS_GROUPS - 1) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n", group_index);
		return -1;
	}

	char grp_name[MAX_MODULE_NAME_LEN];
	sprintf(grp_name, "%s-%d", dev->name, group_index);

	Group *grp = create_group(DEV_ID_EMU_FS, group_index, grp_name,
							  on_framesource_group_data_update);
	grp->device = dev;
	grp->nr_outputs = 1; /* Fixed */
	dev->groups[group_index] = grp;

	return 0;
}

static int framesource_destroy_group(int group_index)
{
	Framesource *framesource = get_framesource();
	Device *dev = framesource->device;

	if (group_index > NR_MAX_FS_GROUPS - 1) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n\n", group_index);
		return -1;
	}

	Group *grp = dev->groups[group_index];

	if (grp == NULL) {
		IMP_LOG_WARN(TAG, "group-%d has not been created\n", group_index);
		return -1;
	}

	destroy_group(grp, DEV_ID_EMU_FS); /* TODO return value. */
	dev->groups[group_index] = NULL;

	return 0;
}

int EmuFrameSourceInit(void)
{
	int ret = 0;
	int i;

	if(gFramesource){
		return 0;
	}

	gFramesource = alloc_framesource();
	if (gFramesource == NULL){
		IMP_LOG_ERR(TAG, "Failed to alloc framesource!\n");
		return -1;
	}
	if(gFramesource != NULL){
		gFramesource->state = FSCHANNEL_STATE_OPEN;
		/* Init VBM. */
		memset(g_emu_vb, 0, sizeof(g_emu_vb));
		for(i = 0; i < NR_MAX_FS_CHNS; i++){
			sem_init(&g_emu_vb[i].semap, 0, 0);
		}
	}
	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] framesource = %p ~~~~~~~\n", __func__,__LINE__,gFramesource);
	return ret;
}

int EmuFrameSourceExit(void)
{
	int ret = 0;
	int i;

	if(!gFramesource)
		return 0;

	if(gFramesource->state > FSCHANNEL_STATE_OPEN){
		IMP_LOG_ERR(TAG, "Failed to exit, because a Framechannel hasn't be destroy!");
		return -1;
	}

	for(i = 0; i < NR_MAX_FS_CHNS; i++){
		sem_destroy(&g_emu_vb[i].semap);
	}

	free_framesource(gFramesource);
	gFramesource = NULL;

	return ret;
}

/* APIs */
static int IMP_EmuFrameSource_SetChnAttr(uint32_t chn_num, IMPFSChnAttr *chn_attr)
{
	int ret = 0;
	if (chn_attr == NULL) {
		IMP_LOG_ERR(TAG, "%s(): chn_attr is NULL\n",__func__);
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] framesource = %p ~~~~~~~\n", __func__,__LINE__,gFramesource);
	if (chn_num > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chn_num %d\n", __func__, chn_num);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s():[chn%d] FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__, chn_num);
		return -1;
	}

	FSChannel *channel = &framesource->channel[chn_num];

	/* crop ops */
	struct v4l2_cropcap cropcap;

	ret = v4l2_emu_cropcap(chn_num, &cropcap);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to cropcap!\n",__func__, chn_num);
		return -1;
	}

	IMPFSChnCrop *crop = &chn_attr->crop;
	if(crop->enable){
		if(crop->left < cropcap.bounds.left || crop->top < cropcap.bounds.top ||
			crop->width + crop->left > cropcap.bounds.left + cropcap.bounds.width ||
			crop->height + crop->top > cropcap.bounds.height + cropcap.bounds.top){
			IMP_LOG_ERR(TAG, "%s():[chn%d] the parameters of IMPFSChnCrop is invalid!\n",__func__,chn_num);
			return -1;
		}
		/* set crop */
		struct v4l2_crop a = *(struct v4l2_crop *)crop;
		ret = v4l2_emu_set_crop(chn_num, &a);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to set crop!\n",__func__, chn_num);
			return -1;
		}
	}

	/* scaler ops */
	EmuV4L2Scalercap sc;
	ret = v4l2_emu_scalercap(chn_num, &sc);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to scalercap!\n",__func__, chn_num);
		return -1;
	}

	IMPFSChnScaler *scaler = &chn_attr->scaler;
	if(scaler->enable){
		if(scaler->outwidth > sc.maxwidth || scaler->outwidth < sc.minwidth ||
			scaler->outheight > sc.maxheight || scaler->outheight < sc.minheight){
			IMP_LOG_ERR(TAG, "%s():[chn%d] the parameters of IMPFSChnScaler is invalid!\n",__func__, chn_num);
			return -1;
		}
		/* set scaler */
		EmuV4L2Scaler sscaler;
		sscaler.outwidth = scaler->outwidth;
		sscaler.outheight = scaler->outheight;
		ret = v4l2_emu_set_scaler(chn_num, &sscaler);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to set scaler!\n",__func__, chn_num);
			return -1;
		}
	}

	/* try format */
	struct v4l2_format *fmt = &channel->fmt;
	fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt->fmt.pix.field = V4L2_FIELD_ANY;
	fmt->fmt.pix.width = chn_attr->picWidth;
	fmt->fmt.pix.height = chn_attr->picHeight;
	fmt->fmt.pix.pixelformat = imppixfmt_to_v4l2pixfmt(chn_attr->pixFmt);

	ret = v4l2_emu_try_fmt(chn_num, fmt);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to tryfmt!\n",__func__, chn_num);
		return -1;
	}

	ret = v4l2_emu_set_fmt(chn_num, fmt);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "%s():[chn%d] Failed to setfmt!\n",__func__, chn_num);
		return -1;
	}

	channel->attr = *chn_attr;

	return 0;
}

int IMP_EmuFrameSource_GetChnAttr(uint32_t chn_num, IMPFSChnAttr *chn_attr)
{
	if (chn_attr == NULL) {
		IMP_LOG_ERR(TAG, "%s(): chn_attr is NULL\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] framesource = %p ~~~~~~~\n", __func__,__LINE__,gFramesource);
	if (chn_num > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chn_num %d\n", chn_num);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n");
		return -1;
	}

	FSChannel *channel = &framesource->channel[chn_num];

	if (channel->attr.picWidth == 0) {
		IMP_LOG_ERR(TAG, "%s(): chn_attr was not set yet\n");
		return -1;
	} else {
		*chn_attr = channel->attr;
	}

	return 0;
}


int IMP_EmuFrameSource_CreateChn(uint32_t chn_num, IMPFSChnAttr *chn_attr)
{
	Framesource *framesource = get_framesource();
	Device *dev = framesource->device;
	FSChannel *chan = NULL;
	char fd_name[32];
	int ret = 0;

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] framesource = %p ~~~~~~~\n", __func__,__LINE__,gFramesource);
	if (chn_num > NR_MAX_FS_CHNS - 1) {
		IMP_LOG_ERR(TAG, "Invalid channel num%d\n", chn_num);
		return -1;
	}

	chan = &framesource->channel[chn_num];
	if(chan->state < FSCHANNEL_STATE_OPEN){
		sprintf(fd_name, EMU_IMAGE_PATH "emu-img-video%d.bin", chn_num);
		chan->img_fd = open(fd_name, O_RDWR, 0);
		if(chan->img_fd < 0){
		//	chan->img_fd = -(chn_num + 1);
			IMP_LOG_ERR(TAG, "Failed to open channel num%d\n", chn_num);
			return -1;
		}

		ret = framesource_create_group(chn_num);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "Failed to create group%d\n", chn_num);
			goto failed_creategroup;
		}

		chan->index = chn_num;
		chan->group = dev->groups[chn_num];
		chan->state = FSCHANNEL_STATE_OPEN;
		g_emu_vb[chn_num].flag = chan->img_fd;

		ret = IMP_EmuFrameSource_SetChnAttr(chn_num, chn_attr);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s(): Failed to SetChnAttr!\n",__func__);
			goto failed_setchnattr;
		}

		/* videobuffer manager */
		VBMPoolConfig poolcfg;
		poolcfg.fmt = chan->fmt;
		poolcfg.nr_buffers = chan->attr.nrVBs;

		VBMInterface vbm_interface;
		vbm_interface.getFrame = get_frame;
		vbm_interface.releaseFrame = release_Frame;

		ret = VBMCreatePool(chn_num, &poolcfg, &vbm_interface, framesource);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s(): Failed to VBMCreatePool!\n",__func__);
			goto failed_createpool;
		}

		framesource->state += FSCHANNEL_STATE_OPEN;
	}
	return 0;
failed_createpool:
failed_setchnattr:
	framesource_destroy_group(chn_num);
failed_creategroup:
	return ret;
}

int IMP_EmuFrameSource_DestroyChn(uint32_t chn_num)
{
	Framesource *framesource = get_framesource();
//	Device *dev = framesource->device;
	FSChannel *chan = NULL;

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] framesource = %p ~~~~~~~\n", __func__,__LINE__,gFramesource);
	if (chn_num > NR_MAX_FS_CHNS - 1) {
		IMP_LOG_ERR(TAG, "Invalid channel num%d\n", chn_num);
		return -1;
	}

	chan = &framesource->channel[chn_num];
	if(chan->state > FSCHANNEL_STATE_OPEN){
		IMP_LOG_ERR(TAG, "channel num%d is busy, please disable it firstly\n", chn_num);
		return -1;
	}else if (chan->state == FSCHANNEL_STATE_OPEN){
		VBMFlushPool(chn_num);
		framesource_destroy_group(chn_num);
		VBMDestroyPool(chn_num);
		if(chan->img_fd >= 0)
			close(chan->img_fd);
		chan->group = NULL;
		chan->state = FSCHANNEL_STATE_CLOSE;
		framesource->state -= FSCHANNEL_STATE_OPEN;
	}

	return 0;
}

int IMP_EmuFrameSource_EnableChn(uint32_t chn_num)
{
	int ret = 0;
	if (chn_num > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chn_num %d\n",__func__, chn_num);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n",__func__);
		return -1;
	}

	FSChannel *channel = &framesource->channel[chn_num];
	ret = VBMFillPool(chn_num);
	if(ret < 0){
		IMP_LOG_ERR(TAG, "%s(): Failed to VBMFillPool!\n",__func__);
		goto failed_fillpool;
	}
	/* Init the tick thread. */
	ret = pthread_create(&channel->tid, NULL,
						 frame_pooling_thread, channel);
	if (ret) {
		IMP_LOG_ERR(TAG, "thread create error\n");
		goto failed_pthread;
	}

	channel->state = FSCHANNEL_STATE_RUN;
	framesource->state += FSCHANNEL_STATE_RUN;

	return 0;
failed_pthread:
failed_fillpool:
	return -1;
}

int IMP_EmuFrameSource_DisableChn(uint32_t chn_num)
{
	if (chn_num > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chn_num %d\n", chn_num);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n");
		return -1;
	}

	FSChannel *channel = &framesource->channel[chn_num];

	framesource->state -= FSCHANNEL_STATE_RUN;
	channel->state = FSCHANNEL_STATE_OPEN;
	pthread_cancel(channel->tid);
	pthread_join(channel->tid, NULL);

	return 0;
}
